﻿#define ENABLETIMING

// Original author contact info: Owen Emlen (owene_1998@yahoo.com)
// Note: other individuals may also have contributed to this code
// Project hosted on CodePlex.com as of 1/10/2009 at http://www.codeplex.com/EmlenMud
using System;
using System.ComponentModel;
using System.Reflection;
using BrainTechLLC.ThreadSafeObjects;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;

namespace BrainTechLLC
{
    public static partial class Timing
    {
#if SILVERLIGHT
#else
        public static readonly ThreadSafeLookup<int, Stopwatch> Stopwatches = new ThreadSafeLookup<int, Stopwatch>();
        public static long NanoSecondsPerTick = 0;
        public static object _lockObject = new object();
        
        /// <summary>
        /// Begins timing code execution on the current thread
        /// </summary>
        [Conditional("ENABLETIMING")]
        public static void BeginTiming()
        {
            Stopwatch sw = GetStopwatch();
            sw.Reset();
            sw.Start();
        }

        /// <summary>
        /// Ends timing code execution on the current thread, executing action(double) with the time elapsed as its parameter
        /// </summary>
        /// <param name="a"></param>
        [Conditional("ENABLETIMING")]
        public static void EndTiming(Action<double> a)
        {
            double d = EndTiming();
            a(d);
        }

        [Conditional("ENABLETIMING")]
        public static void EndTiming(string a)
        {
            double d = EndTiming();
            AllStats.GetInstance().AddTime(a, d);
        }

        /// <summary>
        /// Returns the number of nanoseconds per tick
        /// </summary>
        /// <returns></returns>
        public static long GetNanosecondsPerTick()
        {
            if (NanoSecondsPerTick == 0)
            {
                lock (_lockObject)
                {
                    if (NanoSecondsPerTick == 0)
                    {
                        long frequency = Stopwatch.Frequency;
                        NanoSecondsPerTick = (1000L * 1000L * 1000L) / frequency;
                    }
                }
            }

            return NanoSecondsPerTick;
        }

        /// <summary>
        /// End timing on the thread and return the results
        /// </summary>
        /// <returns></returns>
        private static double EndTiming()
        {
            Stopwatch sw = GetStopwatch();
            sw.Stop();

            return Convert.ToDouble(sw.ElapsedTicks * GetNanosecondsPerTick()) / 1000D;
        }

        /// <summary>
        /// Gets a stopwatch specific to the thread that is executing
        /// </summary>
        /// <returns></returns>
        private static Stopwatch GetStopwatch()
        {
            Stopwatch result;
            int key = Thread.CurrentThread.ManagedThreadId;

            if (!Stopwatches.TryGetValue(key, out result) || result == null)
            {
                result = new Stopwatch();
                Stopwatches.Add(key, result);
            }

            return result;
        }
#endif
    }
}